<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="iNmKN" id="iNmKN"><span data-lake-id="u37133609" id="u37133609">典型回答</span></h1>
  <p data-lake-id="u2cccba26" id="u2cccba26"><span data-lake-id="ubc9391e6" id="ubc9391e6">Netty 通过 Reactor 模型基于多路复用器接收并处理用户请求的。多路复用IO模型参考：</span></p>
  <p data-lake-id="u81112b96" id="u81112b96"><span data-lake-id="u1a216488" id="u1a216488" class="lake-fontsize-12" style="color: rgb(0, 0, 0)">​</span><br></p>
  <p data-lake-id="u88fed562" id="u88fed562"><br></p>
  <p data-lake-id="u600bd660" id="u600bd660"><span data-lake-id="u7d9d7a39" id="u7d9d7a39">多路复用就是首先去阻塞的调用系统，询问内核数据是否准备好，如果准备好，再重新进行系统调用，进行数据拷贝。常见的实现有select，epoll和poll三种。</span></p>
  <p data-lake-id="ua464727f" id="ua464727f"><br></p>
  <p data-lake-id="u5243a055" id="u5243a055"><span data-lake-id="u9a6c69dc" id="u9a6c69dc">Netty 的线程模型并不是一成不变的，它实际取决于用户的启动参数配置。通过设置不同的启动参数，Netty支持三种模型，分别是</span><strong><span data-lake-id="uc94498e1" id="uc94498e1" style="color: rgb(18, 18, 18)">Reactor单线程模型、Reactor多线程模型、Reactor主从多线程模型。</span></strong><span data-lake-id="u63a25711" id="u63a25711" style="color: rgb(18, 18, 18)"><br><br></span></p>
  <h1 data-lake-id="fHwym" id="fHwym"><span data-lake-id="u99712d80" id="u99712d80">扩展知识</span></h1>
  <h2 data-lake-id="zER28" id="zER28"><span data-lake-id="u39cfe967" id="u39cfe967">单Reactor单线程模型</span></h2>
  <p data-lake-id="ud2a48544" id="ud2a48544"><br></p>
  <p data-lake-id="uf0085cef" id="uf0085cef"><span data-lake-id="uee10dca2" id="uee10dca2">这是最简单的Reactor模型，当有多个客户端连接到服务器的时候，服务器会先通过线程A和客户端建立连接，</span></p>
  <p data-lake-id="ud8bb39d3" id="ud8bb39d3"><span data-lake-id="u3d4d3f22" id="u3d4d3f22">有连接请求后，线程A会将不同的事件（如连接事件，读事件，写事件）进行分发，譬如有IO读写事件之后，会把该事件交给具体的Handler进行处理。</span></p>
  <p data-lake-id="u6bad428d" id="u6bad428d"><span data-lake-id="uda28abad" id="uda28abad">​</span><br></p>
  <p data-lake-id="ud55571ce" id="ud55571ce"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673197503485-c98a4e56-fb38-4400-b351-5cc8ecbd8a72.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_35%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u10973bb0" id="u10973bb0"><span data-lake-id="u6321030b" id="u6321030b">​</span><br></p>
  <p data-lake-id="ua6100d97" id="ua6100d97"><span data-lake-id="u61a0582f" id="u61a0582f">而线程A，就是我们说的Reactor模型中的Reactor，Reactor内部有一个dispatch（分发器）。【注意，这里的Reactor单线程，主要负责事件的监听和分发】</span></p>
  <p data-lake-id="u1e1b62f8" id="u1e1b62f8"><span data-lake-id="u2f752630" id="u2f752630">此时一个Reactor既负责处理连接请求，又要负责处理读写请求，一般来说处理连接请求是很快的，但是处理具体的读写请求就要涉及字节的复制，相对慢太多了。Reactor正在处理读写请求的时候，其他请求只能等着，只有等处理完了，才可以处理下一个请求。</span></p>
  <p data-lake-id="uc6d91e72" id="uc6d91e72"><span data-lake-id="u7702351c" id="u7702351c">通过一个Reactor线程，只能对应一个CPU，发挥不出来多核CPU的优势。所以，一个Reactor线程处理简单的小容量场景，还是OK的，但是对于高负载来说，还需要进一步升级。</span></p>
  <h2 data-lake-id="hQm4l" id="hQm4l"><span data-lake-id="uf2136507" id="uf2136507">单Reactor多线程模型</span></h2>
  <p data-lake-id="u7554b147" id="u7554b147"><br></p>
  <p data-lake-id="uc2f4b6a6" id="uc2f4b6a6"><span data-lake-id="u5170cfcb" id="u5170cfcb">为了利用多核CPU的优势，也为了防止在Reactor线程等待读写事件时候浪费CPU，所以可以增加一个Worker的线程池，由此升级为单Reactor多线程模式。</span></p>
  <p data-lake-id="u3759c024" id="u3759c024"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673198436469-79f37e08-715a-4971-b9f2-6a5c12ed830c.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ue1eef486" id="ue1eef486"><span data-lake-id="u110424f6" id="u110424f6">整体流程如下：</span></p>
  <p data-lake-id="uc2f69688" id="uc2f69688"><span data-lake-id="u367ae30d" id="u367ae30d">​</span><br></p>
  <p data-lake-id="u251aa4ef" id="u251aa4ef"><span data-lake-id="uc133d646" id="uc133d646">当多个客户端进入服务器后，Reactor线程会监听多种事件（如连接事件，读事件，写事件），如果监听到连接事件，则把该事件分配给acceptor处理，如果监听到读事件，那么则会发起系统调用，将数据写入内存，之后再把数据交给工作线程池进行业务处理。</span></p>
  <p data-lake-id="ue2035f77" id="ue2035f77"><span data-lake-id="u7d592f49" id="u7d592f49">这个时候我们会发现，业务处理的逻辑已经变成多线程处理了。不过一个Reactor既要负责连接事件，又要负责读写事件，同时还要负责数据准备的过程。因为拷贝数据是阻塞的，假如说Reactor阻塞到拷贝数据的时候，服务器进来了很多连接，这个时候，这些连接是很有可能会被服务器拒绝掉的。</span></p>
  <p data-lake-id="u43123700" id="u43123700"><span data-lake-id="u90aea922" id="u90aea922">所以，单个Reactor看来是不够的，我们需要使用多个Reactor来处理。</span></p>
  <h2 data-lake-id="eVwaQ" id="eVwaQ"><span data-lake-id="uca5796b7" id="uca5796b7">主从Reactor模型</span></h2>
  <p data-lake-id="u53613a54" id="u53613a54"><br></p>
  <p data-lake-id="ua279e962" id="ua279e962"><span data-lake-id="u69ccce37" id="u69ccce37">在主从Reactor模型中，主Reactor线程只负责连接事件的处理，它把读写事件全部交给了子Reactor线程。这样即使在数据准备阶段子线程被阻塞，主Reactor还是可以处理连接事件。巧妙的解决了高负载下的连接问题。</span></p>
  <p data-lake-id="ueb4c35d5" id="ueb4c35d5"><span data-lake-id="u5f4d1cad" id="u5f4d1cad">​</span><br></p>
  <p data-lake-id="u34b69498" id="u34b69498"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673198442513-8eda5fc5-69f0-4b46-8910-661617f0ada1.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_27%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
 </body>
</html>